header {
	#include "../Poliz.h"
	#include <iostream>
	#include <string>
	#include <map>
	#include <sstream>
	#include "../elements/Binary.h"
	#include "../elements/Operation.h"
	#include "../elements/Assign.h"
	#include "../elements/Goto.h"
	#include "../elements/GotoIfFalse.h"
	#include "../elements/Halt.h"
	#include "../elements/Link.h"
	#include "../elements/Number.h"
	#include "../elements/Read.h"
	#include "../elements/String.h"
	#include "../elements/VarReference.h"
	#include "../elements/VarValue.h"
	#include "../elements/Write.h"
	#include "../elements/Writer.h"
	using namespace std;
}
options {
	language = "Cpp";
}
class SimpleParser extends Parser;


options {
	k = 2;
	defaultErrorHandler=false;
}

{
	Poliz poliz;
	map<string,int> ret_vars;
	public: const Poliz &getPoliz() const { return poliz; }
	        map<string,int> getVars() const { return ret_vars; }
	private: void call_procedure(string proc_name) {
		string ret = Link::createFreshLinkName(); poliz.append(Link(ret, poliz));
	poliz.append(Link(proc_name, poliz)); poliz.append(Goto()); poliz.saveLink(ret, poliz.endPosition());
	}
}

program
	: {poliz = Poliz(); call_procedure("main"); poliz.append(Halt()); ret_vars.clear(); } 
	(var_declaration)* (proc_declaration)+ 
	;
// semantics: there must be procedure `main'
// semantics: all variables are different
// semantics: all procedures are different
	
var_declaration
	: "VAR" id:ID ASSIGN n:NUMBER SEMICOLON 
	{ stringstream s(n->getText()); int x; s >> x; ret_vars[id->getText()] = x;}
	;
	
proc_declaration
	: "PROCEDURE" id:ID {poliz.saveProcedure(id->getText(), poliz.endPosition());}
	 "IS" body "END" ID SEMICOLON {poliz.append(Goto());} ;
// translate: [ID] <<body>> <RETURN>
	
body
	: (statement)* ;
// translate: <<statement>>*
	
statement
	: if_statement
	| while_statement
	| read_statement
	| write_statement
	| proc_invocation
	| assignment
	;
	
if_statement
{ string t = Link::createFreshLinkName(); string te = Link::createFreshLinkName(); }
	: "IF" condition {poliz.append(Link(t, poliz)); poliz.append(GotoIfFalse());}
      "THEN" body {poliz.append(Link(te, poliz)); poliz.append(Goto()); poliz.saveLink(t, poliz.endPosition());}
      "ELSE" body "END" "IF" SEMICOLON {poliz.saveLink(te, poliz.endPosition());} ;
// translate: <<condition>> <LINK(t)> <!F> <<body-then>> <LINK(te)> <!> [t] <<body-else>> [te]
	
while_statement
{ string t = Link::createFreshLinkName(); string te = Link::createFreshLinkName();}
	: {poliz.saveLink(t, poliz.endPosition());}
	 "WHILE" condition {poliz.append(Link(te, poliz)); poliz.append(GotoIfFalse());}
     "DO" body "END" "WHILE" SEMICOLON
     {poliz.append(Link(t, poliz)); poliz.append(Goto()); poliz.saveLink(te, poliz.endPosition());} ;
// translate: [t] <<condition>> <LINK(te)> <!F> <<body>> <LINK(t)> <!> [te]

read_statement
	: "READ" id:ID SEMICOLON 
	{poliz.append(VarReference(id->getText())); 
	  poliz.append(Read()); }
	;
// semantics: ID is defined as variable
// translate: <@ID> <READ>
	
write_statement
	: "WRITE" id:ID SEMICOLON {poliz.append(VarValue(id->getText())); poliz.append(Write(WriteInt()));}   // semantics: ID is defined as variable
	| "WRITE" s:STRING SEMICOLON {poliz.append(String(s->getText().substr(1, s->getText().length()-2))); poliz.append(Write(WriteString()));}
	| "WRITELN" SEMICOLON {poliz.append(Write(WriteLn()));}
	;
// translate: <ID> <WRITEINT>
// translate: <STRING> <WRITESTR>
// translate: <WRITELN>

proc_invocation
	: "CALL" id:ID SEMICOLON {call_procedure(id->getText());};
// semantics: ID is defined as a procedure
// translate: <LINK(ID)> <CALL>

assignment
	: id:ID ASSIGN {poliz.append(VarReference(id->getText()));} expression SEMICOLON {poliz.append(Assign());} ;
// semantics: ID is defined as a variable
// translate: <@ID> <<expr>> <ASSIGN>
	
condition
{Binary *op = 0;}
	: expression (EQ{op = new Equality();} | NEQ {op = new NotEquality();} | LESS {op = new Less();} | GREQ{op = new NotLess();}) expression 
	{poliz.append(*op); delete op;}
	;
// translate: <<expression1>> <<expression2>> <<operator>>
	
expression
{Binary *op = 0;}
	: term ( (PLUS {op = new Addition();} | MINUS{op = new Subtraction();}) term 
	{poliz.append(*op); delete op;} )? ;
// translate: <<term1>> <<term2>> <<operator>>
// translate: <<term>>
	
term
	: id:ID {poliz.append(VarValue(id->getText()));}   // semantics: ID is defined as a variable
	| num:NUMBER {stringstream s(num->getText()); int x; s >> x; poliz.append(Number(x));}
	;
// translate: <ID>
// translate: <NUMBER>

class SimpleLexer extends Lexer;

options {
	k = 2;
}

COLON : ":" ;
SEMICOLON : ";" ;
ASSIGN : ":=" ;
PLUS : "+" ;
MINUS : "-" ;
EQ : "=" ;
NEQ : "!=" ;
LESS : "<" ;
GREQ : ">=" ;
	
ID
 :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
    ;

NUMBER :	('0'..'9')+
    ;

WS  :   ( ' ' | '\t' | '\r' | '\n' ) {$setType(antlr::Token::SKIP);} ;
COMMENT : "/*" ( options {greedy=false;} : . )* "*/"  {$setType(antlr::Token::SKIP);} ;

STRING
    :  '"' ( ~('"') )* '"'
    ;
